'app-plug-in 'menu Games/Checkers ' Checkers program ' Written by KC Goldberg and Sons, November, 2004 ' Written in SmallBASIC for Windows, 0.9.5.2 ' This program was written as a teaching example with me and my sons ' We used SmallBASIC mostly because it is cool and free. For this particular ' application, the array structure (including nested arrays) and the ability ' of functions to return an array make this almost LISP-like, which is perfect ' for turn-based games and the MiniMax algorithm ' In addition to the rich array strucure, there is a simple mechanism to ' capture both console input and mouse clicks, and a very accessible ' graphics package. ' Finally, although I have not done this, the ability to port this to my ' PalmPilot is just too cool! ' The program here will either ask the user to make a move using a ' "click on the board" interface, or generate one itself using the ' MiniMax algorithm. ' To add different board evaluators, look into function EvaluateBoard ' and Function DetermineMove for different ply depths ' ===================================================================== ' Data types: ' Board: Array Squares x Squares of pieces ' B(1,1) = lower left ' B(Squares,Squares) = upper right ' Piece: Integer; -2,2: King; -1,1 Piece; 0 Blank ' Move: Array of 4x1 listing coordinates (from, to) e.g. [x1,y1,x2,y2] ' MoveList: An array containing moves e.g. [[x1,y1,x2,y2],[x2,y2,x3,y3], ...] ' To move a piece, a move exists for each intermediate step as well; a jump ' will imply three elements - initial, the square of the piece being jumped, ' and the termination; a double jump will have five elements, etc. ' MoveListArray: An array of MoveLists ' MiniMaxResult: [Score, Move] where score is a numeric board evaluation Score ' =========================================================================== ' Procedures: ' DisplayBoard (Board) : Display a board ' Functions: ' MakeMove (Board, MoveList) = Board : Apply a MoveList to a Board ' GenerateMoveList (Board, Side) = MoveListArray : Generate list of all valid moves for Side ' GetUserMove (Board, Side) = MoveList : Ask user to move for Side ' InitBoard = Board : Return a new board with pieces in initial position ' Func MiniMax(Board, Side, Depth, DepthMax, Evaluator) = MiniMaxResult ' : Perform MiniMax algorithm search to find best move for Side (see declaration ' for description of all parameters) ' EvaluateBoard (Board, Side, Evaluator) = Score : Return a numeric score with positive ' numbers favoring Side using the evaluation function specified by Evaluator ' This should only be called from within MiniMax; technically I should probably make ' it a private function of Minimax, but as it too has private functions, that seemed ' like a lot of overhead for Minimax, which will be called recursively ' DetermineMove (Board, Side) = MoveList : Use the method stored in PlayerStrategy(Side) ' to determine the next move for Side to make ' Main : Main procedure loop ' ' =================================================================== ' Constants ' Size of the board (default = 8 for standard checkers) Const Squares = 8 ' Number of rows to fill in at start (default = 3 for standard checkers) Const InitialRows = 3 ' These constants denote what occupies a piece on any board square Const Piece = 1 Const King = 2 Const Blank = 0 ' This defines the sides Const Black = -1 Const White = 1 ' Figure out how big the boxes should be for graphical display, in pixels ' (board will be square) Const BoxSize = Min(xmax, ymax) / Squares ' Colors for display ' Color of board squares (background) Const ColorDarkSquare = 0 ' Black Const ColorLightSquare = 4 ' Dark Red Const ColorSelect = 1 ' Deep Blue Const ColorSelectFinal = 9 ' Bright blue ' Color of pieces on squares Const ColorBlack = 8 ' Dark Gray Const ColorWhite = 15 ' White ' ========================================================================= ' ========================================================================= ' ------------------------------------------------------------------------- ' This will Generate a Board to the game starting values Func InitBoard ' Variables we will be using Local x,y Dim Board (1 to Squares, 1 to Squares) ' First, clear out board For x = 1 to Squares For y = 1 to Squares Board(x,y) = Blank Next y Next x ' Now, put pieces in place ' We will only put pieces on the "dark" squares, which ' will be identified by the property that (x+y) is even ' i.e. (x+y) Mod = 2 ' e.g. (1,1), (1,3), (2,2), ... For y = 1 to InitialRows For x = 1 to Squares ' White squares go on the "bottom" InitialRows squares If (x+y) Mod 2 = 0 then Board(x,y) = White EndIf ' Black squares go on the "top" InitialRows squares If (x + ((Squares+1) - y)) Mod 2 = 0 then Board (x, ((Squares+1) - y)) = Black Endif Next x Next y InitBoard = Board End ' ------------------------------------------------------------------- ' This routine will display a graphic square with the checkers pieces ' kept in board B Sub DisplayBoard (Board) ' Variables we will be using ' x and y for counters, FillColor a color Local x, y, FillColor ' Clear screen color 0,15 Cls ' Draw the background squares For x = 1 to Squares For y = 1 to Squares Rect (x-1)*BoxSize, (Squares-y)*BoxSize, & x*BoxSize, ((Squares + 1)-y)*BoxSize, & Color IF((x+y) Mod 2 = 0, ColorDarkSquare, ColorLightSquare) & Filled Next y Next x ' Draw the pieces as circles 75% the size of the squares For x = 1 to Squares For y = 1 to Squares If Board(x,y) <> Blank then ' Choose color for circle If Sgn(Board(x,y)) = White then FillColor = ColorWhite Elseif Sgn(Board(x,y)) = Black then FillColor = ColorBlack EndIf ' Draw the checker Circle (x-0.5)*BoxSize, ((Squares+0.5)-y)*BoxSize, & BoxSize*0.375, & 1 , Color FillColor Filled ' If the piece is a king, draw a black circle in the center ' and another colored concentric circle and a final central ' black circle If Abs(Board(x,y)) = King then Circle (x-0.5)*BoxSize, ((Squares+0.5)-y)*BoxSize, & BoxSize*0.275, & 1 , Color 0 Filled Circle (x-0.5)*BoxSize, ((Squares+0.5)-y)*BoxSize, & BoxSize*0.175, & 1 , Color FillColor Filled Circle (x-0.5)*BoxSize, ((Squares+0.5)-y)*BoxSize, & BoxSize*0.075, & 1 , Color 0 Filled Endif EndIf Next y Next x End ' ------------------------------------------------------------------- ' This function is passed a Board and a MoveList and returns the ' board after that series of moves is made ' Note: When jumping, the MoveList should move the piece only one ' square at a time - which will include driving right over the ' enemy piece ' If ShowMoves = True, then display intermediate moves and play ' appropriate tunes, allowing the user to follow along Func MakeMove(Board, MoveList, ShowMoves) ' Piece is the type of piece we are making ' Move is an array of four coordinates, from/to (x1, y1, x2, y2) Local Piece, Move ' Step through each of the moves in ML For Move in MoveList If ShowMoves Then Paint (Move(0)-1)*BoxSize + 1, (Squares-Move(1))*BoxSize + 1, & ColorSelectFinal EndIf ' Find out what kind of piece we are starting with Piece = Board(Move(0), Move(1)) ' Blank out the square where we started Board(Move(0), Move(1)) = Blank ' Fill in the square we are moving to Board(Move(2), Move(3)) = Piece ' See if it should be a king (should be if it lands in either ' row 1 or row Squares If Move(3) = Squares or Move(3) = 1 then Board(Move(2), Move(3)) = King * Sgn(Piece) Endif If ShowMoves Then Paint (Move(2)-1)*BoxSize + 1, (Squares-Move(3))*BoxSize + 1, & ColorSelectFinal Play "V025O3C" EndIf Next Move If ShowMoves Then DisplayBoard Board Play "O3G" EndIf ' Return the new board MakeMove = Board End ' ------------------------------------------------------------------- ' This function is passed a board and a color and returns a MoveListArray listing ' all valid moves that color Side can make ' If there are jumps, then only jumps will be in the movelist (which is to say, ' if you have a jump, you must take it!) Func GenerateMoveList (Board, Side) ' ------------------------------------------------------------------- ' Takes a board, starting position, and offset and will return TRUE ' if that is a valid move, and false if it is not ' abs(dx) must = abs(dy) Func ValidSingleMove (x, y, dx, dy) ValidSingleMove = False ' Check for single move ' There is a valid move if the square 1 square away from ' Board(x,y) in direction of dx,dy is Blank If ((x+dx) >= 1) and ((x+dx) <= Squares) and & ((y+dy) >= 1) and ((y+dy) <= Squares) Then If Board(x+dx, y+dy) = Blank then ValidSingleMove = True EndIf EndIf End ' ------------------------------------------------------------------- ' This returns an array of Moves (a MoveList); however, these ' moves are NOT a sequence to be execute, just a list of up to ' four possible moves, which is then processed by FindAllJumps Func FindJumps (Board, x, y) Local MoveList, dx Erase MoveList For dx in [-1, 1] ' All pieces can jump "forward" ' First, check that coordinates of destination square are legal If ((x + dx*2) >= 1) And ((x + dx*2) <= Squares) And & ((y+2*Side) >= 1) And ((y+2*Side) <= Squares) Then ' Then, check if destination square is open and ' that intermediate square is enemy piece If (Board(x+dx*2,y+2*Side)=Blank) And & (Sgn(Board(x+dx,y+side))=-Side) Then ' If we get here, we have a valid jump; add it ' to the movelist MoveList << & [[x, y, x+dx, y+Side], [x+dx, y+Side, x+dx*2, y+Side*2]] EndIf EndIf ' Only Kings can jump "backwards" If Abs(Board(x,y)) = King then If ((x + dx*2) >= 1) And ((x + dx*2) <= Squares) And & ((y-2*Side) >= 1) And ((y-2*Side) <= Squares) Then ' Then, check if destination square is open and ' that intermediate square is enemy piece If (Board(x+dx*2,y-2*Side)=Blank) And & (Sgn(Board(x+dx,y-side))=-Side) Then ' If we get here, we have a valid jump; add it ' to the movelist MoveList << & [[x, y, x+dx, y-Side], [x+dx, y-Side, x+dx*2, y-Side*2]] EndIf EndIf EndIf Next Dx FindJumps = MoveList End ' Func FindJumps ' ------------------------------------------------------------------- Func FindAllJumps (x, y) Local MoveList, MoveListArray, Move, Board2 Local MoveList2, MoveListArray2 Local MoveList3 Local Counter ' Populate move list with first degree of available jumps MoveListArray = FindJumps(Board, x,y) If Not Empty (MoveListArray) Then ' For each jump, "make" the jump on a temporary Board and ' see if that leads to new jumps; if it does, then ' we have to make a list of NEW jumps with the original ' sequence as a base and delete the initiating sequence Counter = 0 While Not Empty(MoveListArray(Counter)) ' See if there is a move If Not Empty(MoveListArray(Counter)) Then ' OK - start with this MoveList MoveList = MoveListArray(Counter) ' Apply this MoveList to the current Board and ' find where the checker ends up (x2 and y2 from Move) Board2 = MakeMove(Board, MoveList, False) Move = MoveList(UBound(MoveList)) ' Now, see if there are any more jumps from the now terminal ' position MoveListArray2 = FindJumps(Board2, Move(2), Move(3)) ' If there are any, then for each of the MoveLists in MoveListArray2, ' create a new entry in MoveListArray that adds the most recent ' position on to the move before at the end of the array, and ' then delete the current one If Not Empty(MoveListArray2) Then For MoveList2 in MoveListArray2 MoveList3 = MoveList For Move in MoveList2 MoveList3 << Move Next Move MoveListArray << MoveList3 Next MoveList2 ' Get rid of the current MoveList from MoveListArray ' Back counter by one because the initiating sequence ' was deleted Delete MoveListArray, Counter Counter = Counter - 1 EndIf EndIf Counter = Counter + 1 If Counter > UBound(MoveListArray) Then Exit Loop ' The While Loop EndIf Wend EndIf FindAllJumps = MoveListArray End ' Func FindAllJumps ' ------------------------------------------------------------------- ' Function actually begins here ' ' MoveList is an array of Moves Local MoveList ' MoveListArray is an array of MoveLists Local MoveListArray ' MasterMoveListArray is used to keep track of final move lists Local MasterMoveListArray ' Coordinates to move through the board Local x, y, dx Erase MasterMoveListArray ' Start by seeing if there are possible jumps For x = 1 to Squares For y = 1 to Squares ' Find a piece, then see if it can do a jump! If Sgn(Board(x, y)) = Side Then ' Start adding legal jumps starting with [x,y] MoveListArray = FindAllJumps(x, y) ' Copy MoveLists from MoveListArray into MasterMoveListArray For MoveList in MoveListArray MasterMoveListArray << MoveList Next MoveList EndIf Next y Next x ' Check to see if there are no jumps were found - only then look for non-jumping ' moves If Empty (MasterMoveListArray) Then ' In this case, search for non-jumping moves For x = 1 to Squares For y = 1 to Squares If Sgn(Board(x,y)) = Side Then ' Check to see if we can move a single square left or right For dx in [-1, 1] If ValidSingleMove (x, y, dx, Side) Then MasterMoveListArray << [[x, y, x+dx, y+Side]] EndIf ' If the piece is a King, also see if we can move backwards If Abs(Board(x,y)) = King then If ValidSingleMove (x, y, dx, -Side) Then MasterMoveListArray << [[x, y, x+dx, y-Side]] EndIf EndIf Next dx EndIf Next y Next x EndIf ' Return the MoveList GenerateMoveList = MasterMoveListArray End ' ------------------------------------------------------------------- ' This function will allow the user to enter a move for a side ' It outputs a MoveList ' Basic sequence: ' 1. Get valid move list ' 2. Repeat until UserMoveList is a complete valid move list ' 3. Redraw board from scratch ' 4. Highlight squares chosen so far (UserMoveList) ' 5. Let user enter a new square (UserMove) ' 6. Confirm that the new move, when added on to growing UserMoveList, is valid ' The working sequence of user moves (kept in UserMoveList) is terminated ' by a Move with last two coordinates 0,0 (e.g. [1,1,0,0]) Func GetUserMove (Board, Side) ' Where the user is actually moving Local UserMove, UserMoveList ' All valid Moves Local ValidMoveListArray ' Working variables Local Move, ClickMove, MoveList, IsValid, Counter, Done, OldClickMove ' -------------------------------------------------------- ' Waits for the user to click on a square and returns ' the x,y board components as (x, y, 0, 0) ' (DataType = Move) ' Also, we will only accept clicks on Dark squares (X+Y) Mod 2 = 0 Func GetSquare ' Enable tracking of mouse Pen on ' Loop until the left mouse button is pressed Repeat Until Pen(0) ' Convert mouse coordinates into Board coordinates ' Pen(1) = X of mouse position "Last mouse button down X" ' Pen(2) = Y of mouse position "Last mouse button down Y" GetSquare = [1 + Int(Pen(1)/BoxSize),Squares - Int(Pen(2)/BoxSize),0,0] ' Stop Mouse mechanism Pen Off End ' --------------------------------------------------------------- ' This will change the background of a square to color SquareColor Sub HighlightSquare (x, y, SquareColor) ' Only highlight a valid square If (x >= 1) and (x <= Squares) And (y >= 1) And (y <= Squares) Then Paint (x-1)*BoxSize + 1, (Squares-y)*BoxSize + 1, SquareColor EndIf End ' Sub HighLightSquare (x,y) ' ------------------------------------------------------------------ ' Function actually starts here ' Initialize variables Erase UserMoveList Erase UserMove ValidMoveListArray = GenerateMoveList (Board, Side) OldClickMove = [0,0,0,0] Done = False Repeat ' Only redraw current board if we are starting with a fresh movelist ' to save time If Empty(UserMoveList) Then DisplayBoard Board ' Inform use of which side is to move At BoxSize * (Squares + 0.25), TextHeight (PlayerName(Side)) ' Set text color to match If Side = Black Then Color ColorBlack, ColorDarkSquare Else Color ColorWhite, ColorDarkSquare EndIf Print PlayerName(Side) ; " to move" EndIf ' Highlight squares choses so far ' Although for an incomplete move, [Move(2),Move(3)]=0,0 ' the HighlightSquare function checks for that and will ' only highlight valid coordinates For Move in UserMoveList HighlightSquare Move(0), Move(1), ColorSelect HighlightSquare Move(2), Move(3), ColorSelect Next Move ' Allow user to select a square and highlight it ' Users are only allowed to select "Dark", squares, which ' have the properties that the (x+y) coordinate sum is even Repeat Repeat ClickMove = GetSquare Until (ClickMove(0) + ClickMove(1)) Mod 2 = 0 Until ClickMove <> OldClickMove OldClickMove = ClickMove HighLightSquare ClickMove(0), ClickMove(1), ColorSelect ' If this is the first square selected, then just record it and make sure ' it is valid ' If it is the second, then start adding Moves into UserMoveList ' using the last square checked as the first square in this move If Empty(UserMoveList) Then ' Make sure it is a valid Move IsValid = False ' Compare this move against the opening sequences of all valid moves For MoveList in ValidMoveListArray If MoveList(0)(0)=ClickMove(0) And MoveList(0)(1)=ClickMove(1) Then IsValid = True EndIf Next MoveList ' If this is a valid move, store the coordinates as the first ' move in UserMoveList ' Otherwise, leave UserMoveList Empty and beep and get a new choice If IsValid Then UserMoveList = [ClickMove] Else Beep OldClickMove = [0,0,0,0] EndIf Else ' Check to see if this is the second coordinate in a Move ' If so, complete the first entry in UserMoveList ' If not, create a whole new move with the last coordinates ' in the last entry of UserMoveList serving as the first ' coordinates in the new move, and the just-clicked square ' serving as the second coordinates If (UserMoveList(0)(2)=0) and (UserMoveList(0)(3)=0) then ' Check to see if this is a jump If (Abs(ClickMove(0)-UserMoveList(0)(0)) > 1) Or & (Abs(ClickMove(1)-UserMoveList(0)(1)) > 1) Then UserMoveList(0)(2)=(ClickMove(0)+UserMoveList(0)(0))/2 UserMoveList(0)(3)=(ClickMove(1)+UserMoveList(0)(1))/2 UserMoveList << [UserMoveList(0)(2),UserMoveList(0)(3), & ClickMove(0),ClickMove(1)] Else UserMoveList(0)(2)=ClickMove(0) UserMoveList(0)(3)=ClickMove(1) EndIf Else ' Create a new move by adding on to the end Move = UserMoveList(UBound(UserMoveList)) ' If this is a jump, see if we need to insert an intermediate move If (Abs(Move(2)-ClickMove(0))>1) Or (Abs(Move(3)-ClickMove(1))>1) Then UserMoveList << [Move(2), Move(3), & (Move(2)+ClickMove(0))/2, (Move(3)+ClickMove(1))/2] UserMoveList << [(Move(2)+ClickMove(0))/2, (Move(3)+ClickMove(1))/2, & ClickMove(0), ClickMove(1)] Else UserMoveList << [Move(2), Move(3), ClickMove(0), ClickMove(1)] EndIf EndIf ' Now, see if this is a valid sequence from ValidMoveListArray ' OK - at this point, we have UserMoveList containing ' moves that have been completed so far - test them to ' see if they represent a valid sequence For MoveList in ValidMoveListArray ' Compare each MoveList against TempUserMoveList ' If we can get through one whole sequence, then ' it is valid IsValid = True For Counter = 0 to Min (UBound(MoveList), UBound(UserMoveList)) If MoveList(Counter) <> UserMoveList(Counter) Then IsValid = False EndIf Next ' If we get here, then we have confirmed that there is a sequence ' of valid moves in TempUserMoveList so far - no need to check for ' more If IsValid then ' Check to make sure that for this move, the UserMoveList is ' NOT a superset (ie UBound(UserMoveList) > UBound(MoveList)) ' of MoveList If UBound(UserMoveList) > UBound(MoveList) Then IsValid = False Else Exit For EndIf EndIf Next MoveList ' If we get to here and the move is not valid, beep and get a new ' sequence of moves If Not IsValid Then Beep Erase UserMoveList EndIf EndIf ' Test to see if we have completed a sequence If Not Empty(UserMoveList) Then For MoveList in ValidMoveListArray If UserMoveList = MoveList Then Done = True EndIf Next EndIf Until Done ' Return the move list GetUserMove = UserMoveList End ' Func GetUserMove ' ------------------------------------------------------------------ ' This function processes the Artificial Intelligence aspects for ' computer move generation, using the standard Minimax game algorithm ' recursively ' Because of the nature of MiniMax, it has to return two values: ' a MoveList and a Score. We will define a data structure of ' [Score, MoveList] to hold these ' Board is a board to process; Side is the side whose turn it is to move, ' Depth is CURRENT search depth (initally set to zero), ' DepthMax = Deepest level to explore, Evaluator is used to determine ' which board evaluation function to use (see EvaluateBoard) ' We keep all moves with same score in BestMoveList and choose randomly ' among them to try and avoid loops of repetitive moves Func MiniMax(Board, Side, Depth, DepthMax, Evaluator) Local MoveList, Move, Score, BestScore, NextMini, BestMoveList If Depth > DepthMax Then ' If we have exceeded the specified search depth, ' return the value of our board at this point and ' a null movelist MiniMax = [(EvaluateBoard (Board, Side, Evaluator)), 0] Else ' Find all possible moves for Side with the current Board MoveList = GenerateMoveList (Board, Side) ' If there are no moves, then return the score ' of the current board and a null MoveList If Empty (MoveList) Then MiniMax = [EvaluateBoard (Board, Side, Evaluator), 0] Else ' Find the move that yields the best MiniMax score ' Set initial BestScore to lowest conceivable results, to ' guarantee it will be replaced with an actual result later BestScore = -999999 Erase BestMoveList ' Step through each possible move For Move in MoveList ' Recurse down the search tree for the board that ' would result from making that move NextMini = MiniMax(MakeMove(Board,Move,False), & -Side, Depth+1, DepthMax, Evaluator) ' If that move results in a better MiniMax score, then ' save that result Score = -NextMini(0) If Score >= BestScore Then ' If this ties the current best score, add to the list If Score = BestScore Then BestMoveList << Move Else ' If this is a new best score, set the list to this ' move only and update best score BestScore = Score BestMoveList = [Move] EndIf EndIf Next Move ' Return our best move ' Select randomly from the list of moves with an equal / best score MiniMax = [BestScore, BestMoveList(Int(Rnd * UBound(BestMoveList)))] EndIf EndIf End ' Func Minimax ' ------------------------------------------------------------------------ ' This function applies a custom evaluator to be used by MiniMax ' There should be a private function for each different approach ' to scoring the boards, and the main function code will chose among ' them based on Evaluator Func EvaluateBoard (Board, Side, Evaluator) ' ------------------------------------------------------------------ ' This function just adds up the pieces in Board (1=piece,2=King) ' and returns the net sum ' It was the simplest one I could think of Func ScoreSimple(Board, Side) Local x, y, Score Score = 0 For x = 1 to Squares For y = 1 to Squares Score = Score + Board(x,y) Next y Next x ScoreSimple = Score * Side End ' Func ScoreSimple ' ------------------------------------------------------------------------ ' This score adds to SimpleScore by giving bonus for gutter squares ' and a bonus for advancing pieces ' Written by Nat Func ScoreNat (Board, Side) Local x, y, Score ' Start with neutral score Score = 0 For x = 1 to Squares For y = 1 to Squares Score = Score + Board(x,y) ' Bonus for being x = 1 or Squares If (x=1) or (x = Squares) Then Score = Score + 0.5 * Board(x,y) EndIf ' Bonus for advancing to final row for Pieces only If Abs(Board(x,y)) = Piece then If Side = 1 Then Score = Score + 0.125 * y Else Score = Score - 0.125 * ((Squares + 1) - y) EndIf EndIf Next y Next x ScoreNat = Score * Side End ' Func ScoreNat ' ------------------------------------------------------------------------ ' This score adds to SimpleScore by giving penalty for gutter squares ' Written by Sam Func ScoreSam (Board, Side) Local x, y, Score ' Start with neutral score Score = 0 For x = 1 to Squares For y = 1 to Squares Score = Score + Board(x,y) ' Penalty for being x = 1 or Squares If (x=1) or (x = Squares) Then Score = Score - 0.5 * Board(x,y) EndIf ' Penalty for being in corner If ((x=1) or (x=Squares)) And ((y=1) or (y=Squares)) Then Score = Score - 0.5 * Board(x,y) EndIf Next y Next x ScoreSam = Score * Side End ' Func ScoreSam ' ------------------------------------------------------------------------ ' Function begins here ' The idea is that we have multiple Evaluator functions as a test ' of different strategies If Evaluator = 2 Then EvaluateBoard = ScoreSimple (Board, Side) ElseIf Evaluator = 3 Then EvaluateBoard = ScoreNat (Board, Side) ElseIf Evaluator = 4 Then EvaluateBoard = ScoreSam (Board, Side) EndIf End ' Func EvaluateBoard ' ------------------------------------------------------------------- ' This function will determine the next move for Side based on the ' strategy in PlayerStrategy(Side) and return a MoveList Func DetermineMove (Board, Side) Local Temp ' This will display text to the right (by 1/4 square) of the board ' listing who is currently moving ' The color of the text will match the color of the pieces At BoxSize * (Squares + 0.25), TextHeight (PlayerName(Side)) ' Set text color to match If Side = Black Then Color ColorBlack, ColorDarkSquare Else Color ColorWhite, ColorDarkSquare EndIf At BoxSize * (Squares + 0.25), TextHeight (PlayerName(Side)) Print PlayerName(Side) + " to move" If PlayerStrategy(Side) = 1 Then DetermineMove = GetUserMove (Board, Side) ElseIf PlayerStrategy(Side) = 2 Then Temp = MiniMax(Board, Side, 0, 2, 2) DetermineMove = Temp(1) ElseIf PlayerStrategy(Side) = 3 Then Temp = MiniMax(Board, Side, 0, 3, 2) DetermineMove = Temp(1) ElseIf PlayerStrategy(Side) = 4 Then Temp = MiniMax(Board, Side, 0, 4, 2) DetermineMove = Temp(1) ElseIf PlayerStrategy(Side) = 5 Then Temp = MiniMax(Board, Side, 0, 5, 2) DetermineMove = Temp(1) ElseIf PlayerStrategy(Side) = 6 Then Temp = MiniMax(Board, Side, 0, 4, 3) DetermineMove = Temp(1) At (Squares + 0.25) * BoxSize, TextHeight ("1") * 3 Print "Score :"; Temp(0) ElseIf PlayerStrategy(Side) = 7 Then Temp = MiniMax(Board, Side, 0, 4, 4) DetermineMove = Temp(1) At (Squares + 0.25) * BoxSize, TextHeight ("1") * 3 Print "Score :"; Temp(0) EndIf End ' Func DetermineMove ' ------------------------------------------------------------------- ' This is the main control function for the checkers program Sub Main Local Board, MoveList, Winner, Counter ' Get User information: ' For each color, get a unique name for the player and ' a strategy for determining the move to make For Counter in [White, Black] ' Get the name of the players Print "Please enter the name for the "; If Counter = White Then Print "white"; Else Print "black"; EndIf Print " player :"; Input PlayerName (Counter) Print Repeat ' Get the strategy for each player to use Print "1. Ask the user" Print "2. Use SimpleScore Level 2" Print "3. Use SimpleScore Level 3" Print "4. Use SimpleScore Level 4" Print "5. Use SimpleScore Level 5" Print "6. Use NatScore Level 4" Print "7. Use SamScore Level 4" Print "Enter the strategy to use for "; PlayerName(Counter); Input PlayerStrategy(Counter) Until PlayerStrategy(Counter) in [1,2,3,4,5,6,7] Print Print Next Counter ' Initialize the board Board = InitBoard ' Display the board DisplayBoard Board ' Loop until game is over - it is over when one side cannot ' make any more moves While True ' Allow white to move is there is a move for White to make If Empty(GenerateMoveList (Board, White)) Then Winner = Black Exit Loop Else ' Get White Move MoveList = DetermineMove (Board, White) ' Apply White Move Board = MakeMove (Board, MoveList, True) ' Show the new board DisplayBoard Board EndIf ' Allow Black to move if there is a move for Black to make If Empty(GenerateMoveList (Board,Black)) Then Winner = White Exit Loop Else ' Get Black Move MoveList = DetermineMove (Board, Black) ' Apply Black Move Board = MakeMove (Board, MoveList, True) ' Show the new board DisplayBoard Board EndIf Wend ' Now, display the winner At BoxSize * (Squares + 0.25), TextHeight (PlayerName(Winner)) Print PlayerName(Winner) ; " wins!" ' Play a little fanfare and pause for 1 second Play "O2CEGO3CP1" Play "p1" End ' ------------------------------------------------------------------- ' Call the Main procedure, then exit the program ' Declare array of player names Dim PlayerName (Black to White) ' Declare array of strategy choices Dim PlayerStrategy (Black to White) Main End